﻿using System;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Threading;
using System.Windows;

namespace
#if WINDOWS_PHONE
    PServiceBus.WindowsPhone
#else
  PServiceBus.Silverlight
#endif
    
     {
    public class HttpStreaming {

        const int MAX_BUFFER_SIZE = 8;
        private bool _running;
        private byte[] _lastBuffer = default(byte[]);
        private byte[] _bigBuffer = new byte[MAX_BUFFER_SIZE * 2];
        private HttpWebRequest _client;
        private HttpWebResponse _response;
        private MemoryStream _memoryStream = new MemoryStream();
        readonly static byte[] _delimeter = System.Text.Encoding.UTF8.GetBytes("</comet>");
        readonly static Regex _cometRegex =
            new Regex("(<comet)?>(?<Data>.+?)</comet>", RegexOptions.Multiline);
        
        readonly string _url; 
        


        public HttpStreaming(string url) {
            _url = url;
        }

        public void Start() {
            if (_running) throw new InvalidOperationException("Streaming is already in progress");
            _running = true;

            var buffer = new byte[MAX_BUFFER_SIZE];
#if !WINDOWS_PHONE
            _client = System.Net.Browser.WebRequestCreator.BrowserHttp.Create(new Uri(_url)) as HttpWebRequest;
#else
            _client = WebRequest.Create(_url) as HttpWebRequest;
#endif
            _client.Method = "GET";
            _client.AllowReadStreamBuffering = false;

            _client.BeginGetResponse(o =>
            {
                var request = o.AsyncState as HttpWebRequest;
                _response = request.EndGetResponse(o) as HttpWebResponse;
                var stream = _response.GetResponseStream();
                stream.BeginRead(buffer, 0, MAX_BUFFER_SIZE, ReadBuffer,
                    new Tuple<Stream, byte[]>(stream, buffer));
            }, _client);
        }

        public void Stop() {
            if (!_running) return;
            _running = false;
            try {
                _client.Abort();
                _response.Close();
            } catch { }
        }

        private void ReadBuffer(IAsyncResult r) {
            try {
                var tuple = r.AsyncState as Tuple<Stream, byte[]>;
                var stream = tuple.Item1;
                var buffer = tuple.Item2;
                var read = stream.EndRead(r);
                var eof = read == 0 || read == -1;
                if (!eof)
                    _memoryStream.Write(buffer, 0, read);

                if (HasData(buffer)) 
                    ProcessBuffer(_memoryStream.ToArray());
                
                _lastBuffer = new byte[MAX_BUFFER_SIZE];
                Buffer.BlockCopy(buffer, 0, _lastBuffer, 0, read);
                Array.Clear(buffer, 0, MAX_BUFFER_SIZE);
                if (!eof)
                    stream.BeginRead(buffer, 0, MAX_BUFFER_SIZE,
                        ReadBuffer, new Tuple<Stream, byte[]>(stream, buffer));
            } catch { }
        }

        private bool HasData(byte[] buffer) {
            var count = 0;
            for (var i = 0; i < buffer.Length; i++)
                if (buffer[i] == _delimeter[i]) count++;
            if (count == buffer.Length) return true;
            if (_lastBuffer == null) return false;

            Buffer.BlockCopy(_lastBuffer, 0, _bigBuffer, 0, MAX_BUFFER_SIZE);
            Buffer.BlockCopy(buffer, 0, _bigBuffer, MAX_BUFFER_SIZE, MAX_BUFFER_SIZE);
            var delimiterIndex = 0;
            for (var i = 0; i < _bigBuffer.Length; i++) {
                if (_bigBuffer[i] == _delimeter[delimiterIndex]) {
                    if (++delimiterIndex == _delimeter.Length)
                        if (i < MAX_BUFFER_SIZE)
                            return false;
                        else
                            return true;
                } else
                    delimiterIndex = 0;
            }
            return false;
        }

        private void ProcessBuffer(object state) {
            var buffer = state as byte[];
            var text = System.Text.Encoding.UTF8.GetString(buffer, 0, buffer.Length);
            var data = _cometRegex.Match(text).Groups["Data"].Value;
            if (OnReceived != null)
                Deployment.Current.Dispatcher.BeginInvoke(OnReceived, data);
            _memoryStream.SetLength(0);
        }

        public Action<string> OnReceived { get; set; }
    }
#if WINDOWS_PHONE
    public class Tuple<T1, T2> {
        public T1 Item1 { get; internal set; }
        public T2 Item2 { get; internal set; }
        public Tuple(T1 item1, T2 item2) {
            Item1 = item1;
            Item2 = item2;
        }
    }
#endif
}
